from django.db import models
from django.contrib import auth
from django.core.exceptions import ImproperlyConfigured
from django.db import models
from django.db.models.manager import EmptyManager
from django.contrib.contenttypes.models import ContentType
from django.utils.encoding import smart_str
from django.utils.hashcompat import md5_constructor, sha_constructor
from django.utils.translation import ugettext_lazy as _
import datetime

"""
Modulo para la creacion de entidades o tablas para la administracion del sistema.
:author: Marco Rolon, Bianca Benegas
"""

TIPO_CHOICES = (
        ('A', u'Administrativo'),
        ('O', u'Operativo'),
        
)
UNUSABLE_PASSWORD = '!' # This will never be a valid hash

try:
    set
except NameError:
    from sets import Set as set   # Python 2.3 fallback

   
class Permiso(models.Model):
    """ 
    Clase para crear la tabla Permiso con sus respectivos campos.
    Todos los campos son necesarios.
    """
    nombre = models.CharField(_('nombre'), max_length=50)
    descripcion = models.CharField(_('descripcion'), max_length=100)
    tipo = models.CharField(_('tipo'), max_length=1)

    class Meta:
        """ 
        Subclase para asignar el nombre del modelo en singular y plural.
        """
        verbose_name = _('permiso')
        verbose_name_plural = _('permisos')
        #unique_together = (('content_type', 'codename'),)
        #ordering = ('content_type__app_label', 'codename')

    def __unicode__(self):
        return u"%s" % (
            #unicode(self.content_type.app_label),
            #unicode(self.content_type),
            unicode(self.nombre))
			
	
def get_hexdigest(algorithm, salt, raw_password):
    """
    Devuelve una cadena de hexdigest de la contrasenya en texto plano dado y la sal
    utilizando el algoritmo dado ('md5', 'sha1' o 'crypt').
    
    Returns a string of the hexdigest of the given plaintext password and salt
    using the given algorithm('md5', 'sha1' or 'crypt').
    """
    raw_password, salt = smart_str(raw_password), smart_str(salt)
    if algorithm == 'crypt':
        try:
            import crypt
        except ImportError:
            raise ValueError('"crypt" password algorithm not supported in this environment')
        return crypt.crypt(raw_password, salt)

    if algorithm == 'md5':
        return md5_constructor(salt + raw_password).hexdigest()
    elif algorithm == 'sha1':
        return sha_constructor(salt + raw_password).hexdigest()
    raise ValueError("Got unknown password algorithm type in password.")

def check_password(raw_password, enc_password):
    """
    Devuelve un booleano si el raw_password era correcto. Maneja
    formatos de codificacion en segundo plano.
    
    Returns a boolean of whether the raw_password was correct. Handles
    encryption formats behind the scenes.
    """
    algo, salt, hsh = enc_password.split('$')
    return hsh == get_hexdigest(algo, salt, raw_password)


# Create your models here.
class UserManager(models.Manager):
    def create_user(self, username, email, password=None):
        "Creates and saves a User with the given username, e-mail and password."
        now = datetime.datetime.now()
        user = self.model(None, username, '', '', email.strip().lower(), 'placeholder', '', '', True)
        if password:
            user.set_password(password)
        else:
            user.set_unusable_password()
        user.save()
        return user

class Rol(models.Model):
    """ 
    Clase para crear la tabla Rol con sus respectivos campos.
    Todos los campos son necesarios.
    
    Al crear un rol se podran asignar los siguientes permisos:
    
    - Administrador: Puede manejar los tipos de artefactos, permisos y vistas. Tipo Administrativo.
    - Leer Usuario: Puede ver la lista de usuarios y sus datos. Tipo Administrativo.
    - Crear Usuario: Puede crear un nuveo usuario en el sistema. Tipo Administrativo.
    - Modificar Usuario: Puede modificar un usuario. Este permiso incluye Leer Usuario. Tipo Administrativo.
    - Eliminar Usuario: Puede eliminar un usuario. Este permiso incluye Leer Usuario. Tipo Administrativo.
    - Leer Rol: Puede ver la lista de todos los roles y sus datos. Tipo Administrativo.
    - Crear Rol: Puede crear un rol en el sistema. Tipo Administrativo.
    - Modificar Rol: Puede modificar un rol. Tipo Administrativo.
    - Eliminar Rol: Puede eliminar un rol. Tipo Administrativo.
    - Leer Proyecto: Puede ver los proyectos existentes. Tipo Administrativo.
    - Crear Proyecto: Puede crear proyectos. Tipo Administrativo.
    - Modificar Proyecto: Puede modificar un proyecto. Tipo Administrativo.
    - Eliminar Proyecto: Puede eliminar un proyecto. Tipo Administrativo.
    - Leer Tipo Artefacto: Puede leer los tipos de artefactos de un proyecto. Tipo Operativo.
    - Crear Tipo Artefacto: Puede crear un nuevo tipo de artefacto. Tipo Operativo.
    - Modificar Tipo Artefacto: Puede modificar un tipo de artefacto. Tipo Operativo.
    - Eliminar Tipo Artefacto: Puede eliminar tipos de artefactos. Tipo Operativo.
    - Leer Artefacto Analisis: Puede leer artefactos de la fase de analisis. Tipo Operativo.
    - Crear Artefacto Analisis: Puede crear artefactos de la fase de analisis. Tipo Operativo.
    - Modificar Artefacto Analisis: Puede modificar un artefacto de al fase de analisis. Tipo Operativo.
    - Eliminar Artefacto Analisis: Puede eliminar un artefacto de la fase de analisis. Tipo Operativo.
    - Leer Artefacto Disenyo: Puede leer artefactos de la fase de disenyo. Tipo Operativo.
    - Crear Artefacto Disenyo: Puede crear artefactos de la fase de disenyo. Tipo Operativo.
    - Modificar Artefacto Disenyo: Puede modificar artefactos de la fase de disenyo. Tipo Operativo.
    - Eliminar Artefacto Disenyo: Puede eliminar artefactos de la fase de disenyoo. Tipo Operativo.
    - Leer Artefacto Implementacion: Puede leer artefactos de la fase de implementacion. Tipo Operativo.
    - Crear Artefacto Implementacion: Puede crear artefactos de la fase de implementacion. Tipo Operativo.
    - Modificar Artefacto Implementacion: Puede modificar artefactos de la fase de implementacion. Tipo Operativo.
    - Eliminar Artefactos Implementacion: Puede eliminar artefactos de la fase de implementacion. Tipo Operativo.
    - Asignar Rol ProyPuede asignar roles de proyecto. Tipo Operativo.
    - Asignar Rol Admin: Puede asignar roles administrativos. Tipo Administrativo.
    - Generar Linea Base Analisis: Puede generar una linea base de la fase de analisis. Tipo Operativo.
    - Generar Linea Base Disenyo: Puede generar una linea base de la fase de disenyo. Tipo Operativo.
    - Generar Linea Base Implementacion: Puede generar una linea base de la fase de implementacion. Tipo Operativo.
    - Aprobar Artefactos Disenyo: Puede aprobar artefactos de la fase de disenyo. Tipo Operativo.
    - Aprobar Artefacto Analisis: Puede aprobar artefactos de la fase de analisis. Tipo Operativo.
    - Aprobar Artefactos Implementacion: Puede aprobar artefactos de la fase de implementacion. Tipo Operativo.
    - Leer Tipo de Artefacto General: Puede leer los tipos de artefactos generales. Tipo Operativo.
    - Crear Tipo de Artefacto General: Puede crear tipos de artefactos generales. Tipo Operativo.
    - Modificar Tipo de Artefacto General: Puede modificar tipos de artefactos generales. Tipo Operativo.
    - Eliminar Tipo de Artefacto General: Puede eliminar tipos de artefactos Generales. Tipo Administrativo.
    """
    nombre = models.CharField(_('nombre'), max_length=30, unique=True)
    descripcion = models.CharField(_('descripcion'), max_length=200, unique=True)
    tipo = models.CharField(_('tipo'), max_length=1, choices=TIPO_CHOICES)
    rol_permiso = models.ManyToManyField(Permiso, verbose_name=_('rol permiso'), blank=True)
	
    class Meta:
        """ 
        Subclase para asignar el nombre del modelo en singular y plural. 
        """
        verbose_name = _('rol')
        verbose_name_plural = _('roles')
        #unique_together = (('content_type', 'codename'),)
        #ordering = ('content_type__app_label', 'codename')

    def __unicode__(self):
        return u"%s" % (
            unicode(self.nombre))
		

class Usuario(models.Model):
    """
    Clase para crear la tabla Rol con sus respectivos campos.
    Los usuarios dentro del sistema de autenticacion de Django estan representados por este modelo.
    Nombre de usuario y clave son necesarios. Los demas campos son opcionales.
    """
    username = models.CharField(_('username'), max_length=30, unique=True, help_text=_("Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores)."))
    nombre = models.CharField(_('nombre'), max_length=30, blank=True)
    apellido = models.CharField(_('last name'), max_length=30, blank=True)
    email = models.EmailField(_('e-mail'), blank=True)
    password = models.CharField(_('password'), max_length=128, help_text=_("Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change password form</a>."))
    direccion = models.CharField(_('direccion'), max_length=100, blank=True)
    telefono = models.CharField(_('telefono'), max_length=15, blank=True)
    #is_staff = models.BooleanField(_('staff status'), default=False, help_text=_("Designates whether the user can log into this admin site."))
    is_active = models.BooleanField(_('active'), default=True, help_text=_("Designates whether this user should be treated as active. Unselect this instead of deleting accounts."))
    usuario_rol = models.ManyToManyField(Rol, through='UsRolPro', verbose_name=_('usuario rol'), blank=True)
    #is_superuser = models.BooleanField(_('superuser status'), default=False, help_text=_("Designates that this user has all permissions without explicitly assigning them."))
    #last_login = models.DateTimeField(_('last login'), default=datetime.datetime.now)
    #date_joined = models.DateTimeField(_('date joined'), default=datetime.datetime.now)
    #groups = models.ManyToManyField(Group, verbose_name=_('groups'), blank=True,
     #   help_text=_("In addition to the permissions manually assigned, this user will also get all permissions granted to each group he/she is in."))
    #usuario_rol = models.ManyToManyField(Rol, verbose_name=_('usuario rol'), blank=True)
    objects = UserManager()

    class Meta:
        """ Subclase para asignar el nombre del modelo en singular y plural. """
        verbose_name = _('usuario')
        verbose_name_plural = _('usuarios')

    def __unicode__(self):
        return self.username

    def get_absolute_url(self):
        return "/users/%s/" % urllib.quote(smart_str(self.username))

    def is_anonymous(self):
        "Always returns False. This is a way of comparing User objects to anonymous users."
        return False

    def is_authenticated(self):
        """
        Siempre devolvera True. Esta es una manera de saber si el usuario se ha autenticado en las
        plantillas.
        ***
        Always return True. This is a way to tell if the user has been authenticated in templates.
        """
        return True

    def get_full_name(self):
        "Returns the first_name plus the last_name, with a space in between."
        full_name = u'%s %s' % (self.nombre, self.apellido)
        return full_name.strip()

    def set_password(self, raw_password):
        import random
        algo = 'sha1'
        salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5]
        hsh = get_hexdigest(algo, salt, raw_password)
        self.password = '%s$%s$%s' % (algo, salt, hsh)

    def check_password(self, raw_password):
        """
        Devuelve un booleano si el raw_password era correcto. Maneja
        formatos de codificacion en segundo plano.
        
        Returns a boolean of whether the raw_password was correct. Handles
        encryption formats behind the scenes.
        """
        # Backwards-compatibility check. Older passwords won't include the
        # algorithm or salt.
        if '$' not in self.password:
            is_correct = (self.password == get_hexdigest('md5', '', raw_password))
            if is_correct:
                # Convert the password to the new, more secure format.
                self.set_password(raw_password)
                self.save()
            return is_correct
        return check_password(raw_password, self.password)

    def set_unusable_password(self):
        # Sets a value that will never be a valid hash
        self.password = UNUSABLE_PASSWORD

    def has_usable_password(self):
        return self.password != UNUSABLE_PASSWORD

    def get_group_permissions(self):
        """
        Devuelve una lista de cadenas de permisos que el usuario tiene a traves de
        sus grupos. Este metodo consulta todos los backends de autenticacion disponibles.
        
        Returns a list of permission strings that this user has through
        his/her groups. This method queries all available auth backends.
        """
        permissions = set()
        for backend in auth.get_backends():
            if hasattr(backend, "get_group_permissions"):
                permissions.update(backend.get_group_permissions(self))
        return permissions

    def get_all_permissions(self):
        permissions = set()
        for backend in auth.get_backends():
            if hasattr(backend, "get_all_permissions"):
                permissions.update(backend.get_all_permissions(self))
        return permissions

    def has_perm(self, perm):
        """
        Devuelve True si el usuario tiene el permiso especificado. Este metodo
        consulta todos los backends de autenticacion disponibles, pero regresa
        inmediatamente si cualquier backend devuelve True. Asi, un usuario que
        tiene permiso de una sola autenticacion se supone que tiene permiso en 
        general.
        
        Returns True if the user has the specified permission. This method
        queries all available auth backends, but returns immediately if any
        backend returns True. Thus, a user who has permission from a single
        auth backend is assumed to have permission in general.
        """
        # Inactive users have no permissions.
        if not self.is_active:
            return False 
        roles= self.usuario_rol.all()#todos los roles que tiene ese usuario
        if perm.tipo == 'A':
            admin = Rol.objects.get(id=1)
           
            if admin in roles:
                return True
        for rol in roles:
            permisos=rol.rol_permiso.all()
            #if perm.tipo=='A':
            #    if Permiso.objects.get(id=1) in permisos:
            if perm in permisos:
                return True
            #for permiso in permisos:
            #    if permiso==perm:#rol de admin
            #        return True
        return False

    def has_perms(self, perm_list):
        """
        Devuelve True si el usuario tiene cada uno de los permisos especificados.
        ***
        Returns True if the user has each of the specified permissions.
        """
        for perm in perm_list:
            if not self.has_perm(perm):
                return False
        return True

    def has_module_perms(self, app_label):
        """
        Returns True if the user has any permissions in the given app
        label. Uses pretty much the same logic as has_perm, above.
        """
        if not self.is_active:
            return False

        if self.is_superuser:
            return True

        for backend in auth.get_backends():
            if hasattr(backend, "has_module_perms"):
                if backend.has_module_perms(self, app_label):
                    return True
        return False

    def get_and_delete_messages(self):
        messages = []
#        for m in self.message_set.all():
#            messages.append(m.message)
#            m.delete()
        return messages

    def email_user(self, subject, message, from_email=None):
        "Sends an e-mail to this User."
        from django.core.mail import send_mail
        send_mail(subject, message, from_email, [self.email])

    def get_profile(self):
        """
        Devuelve el perfil sitio-especifico de este usuario. Eleva
        SiteProfileNotAvailable si este sitio no permite los perfiles.
        
        Returns site-specific profile for this user. Raises
        SiteProfileNotAvailable if this site does not allow profiles.
        """
        if not hasattr(self, '_profile_cache'):
            from django.conf import settings
            if not getattr(settings, 'AUTH_PROFILE_MODULE', False):
                raise SiteProfileNotAvailable
            try:
                app_label, model_name = settings.AUTH_PROFILE_MODULE.split('.')
                model = models.get_model(app_label, model_name)
                self._profile_cache = model._default_manager.get(user__id__exact=self.id)
                self._profile_cache.user = self
            except (ImportError, ImproperlyConfigured):
                raise SiteProfileNotAvailable
        return self._profile_cache

from saip.proyecto.models import Proyecto
class UsRolPro(models.Model):
    """ 
    Clase para crear la tabla intermedia UsRolPro con sus respectivos campos. 
    Todos los campos son necesarios.
    """
    usuario = models.ForeignKey(Usuario)
    rol = models.ForeignKey(Rol)
    proyecto = models.ForeignKey(Proyecto, blank=True, null=True)

